package com.ev.framework.aspect;

import com.alibaba.fastjson.JSON;
import com.ev.framework.annotation.EvApiByToken;
import com.ev.framework.annotation.Log;
import com.ev.framework.utils.IPUtils;
import com.ev.framework.utils.ShiroUtils;
import com.ev.framework.utils.StringUtils;
import com.ev.system.domain.ExcLogDO;
import com.ev.system.domain.OpLogDO;
import com.ev.system.service.ExcLogService;
import com.ev.system.service.OpLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
 * 操作日志切片
 */
@Aspect
@Component
public class LogEviAnnotationAspect {
    private static final String[] INSERT = {"增", "保", "导入","提交"};

    private static final String[] UPDATE = {"修", "改", "编辑"};

    private static final String[] DELETE = {"删"};

    private static final String[] AUDIT = {"审", "核"};

    private static final String[] QUERY = {"获取","列", "查" , "导出","详","数据"};

    private static final String[] IGNORE = {"getMenus","logout"};

    @Autowired
    OpLogService opLogService;

    /**
     * 设置操作日志切入点 记录操作日志 在注解的位置切入代码
     */
    @Pointcut("@annotation(com.ev.framework.annotation.EvApiByToken)")
    public void operLogPoinCut() {
    }

    /**
     * 正常返回通知，拦截用户操作日志，连接点正常执行完成后执行， 如果连接点抛出异常，则不会执行
     *
     * @param joinPoint 切入点
     * @param keys      返回结果
     */
    @AfterReturning(value = "operLogPoinCut()", returning = "keys")
    public void saveOperLog(JoinPoint joinPoint, Object keys) {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        OpLogDO operlog = new OpLogDO();
        try {

            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            Api api = joinPoint.getTarget().getClass().getAnnotation(Api.class);
            String[] tags = api.tags();
            // 操作模块
            operlog.setOpModule(tags[0]);
            // 获取请求的方法名
            String methodName = method.getName();
            // 忽略掉不记录的方法
            for (String s : IGNORE) {
                if (Objects.equals(methodName,s)){
                    return;
                }
            }
            if (className.startsWith("com.ev.apis.controller.report")){
                return;
            }
            methodName = className + "." + methodName;

            operlog.setOpMethod(methodName); // 请求方法
            // 获取操作
            EvApiByToken evApiByToken = method.getAnnotation(EvApiByToken.class);
            if (evApiByToken != null) {
                ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                // 操作描述
                String opDesc = apiOperation.value();
                // 设置类型
                String opType = generateOpType(opDesc);
                // XXX 若是查询类型则不记录
                if ("".equals(opType) || Objects.equals(opType, "查询")) {
                    return;
                }
                operlog.setOpType(opType); // 操作类型
                operlog.setOpDesc(opDesc); // 操作描述
            }


            // 请求的参数
            Map<String, String> rtnMap = converMap(request.getParameterMap());
            String params;
            if(rtnMap.size() == 0){
                // requestBody 传参
                params = JSON.toJSONString(joinPoint.getArgs());
            }else {
                // requestParam 传参
                // 将参数所在的数组转换成json
                params = JSON.toJSONString(rtnMap);
            }
            String opUserName = ShiroUtils.getUser()==null?null:ShiroUtils.getUser().getUsername();
            if (opUserName != null) {
                operlog.setOpRequParam(params); // 请求参数
                operlog.setOpRespParam(JSON.toJSONString(keys)); // 返回结果
                operlog.setOpUserId(ShiroUtils.getUserId()); // 请求用户ID
                operlog.setOpUserName(opUserName); // 请求用户名称
                operlog.setOpIp(IPUtils.getIpAddr(request)); // 请求IP
                operlog.setOpUri(request.getRequestURI()); // 请求URI
                operlog.setCreateTime(new Date()); // 创建时间
                opLogService.save(operlog);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String generateOpType(String opDesc) {
        if (StringUtils.isEmpty(opDesc)){
            return "";
        }
        for (String s : QUERY) {
            if (opDesc.contains(s)) {
                return "查询";
            }
        }
        for (String s : INSERT) {
            if (opDesc.contains(s)) {
                return "增加";
            }
        }
        for (String s : UPDATE) {
            if (opDesc.contains(s)) {
                return "修改";
            }
        }
        for (String s : DELETE) {
            if (opDesc.contains(s)) {
                return "删除";
            }
        }
        for (String s : AUDIT) {
            if (opDesc.contains(s)) {
                return "审核";
            }
        }

        return "其他";
    }


    /**
     * 转换request 请求参数
     *
     * @param paramMap request获取的参数数组
    */
    public Map<String, String> converMap(Map<String, String[]> paramMap) {
        Map<String, String> rtnMap = new HashMap<>();
        for (String key : paramMap.keySet()) {
            rtnMap.put(key, paramMap.get(key)[0]);
        }
        return rtnMap;
    }
}
